(use ./common)
(defn marsh-fn "Wrap a function to take a marshalled buffer as its args."
  (fx) #(*)=>*
  (fn marshalled-fx (s) #s
    (def [res val] (protect
      (unmarshal s)))
     (if res (fx ;val)
         (printf "[%s] WARNING: Failed to unmarshal %M, skip\n" (os/strftime "%y/%m/%d r %H:%M:%S") val))))

(defn protected-fn (fx)
   (fn protected-fx (& args)
     (protect (fx ;args))))

(defn fmarsh-fn "Wrap a function to take a marshalled buffer and return a marshalled buffer."
  (fx) #(*)=>*
   (defn marshalled-fx (marsh-fn fx))
  (fn fully-marshalled-fx (s) #s
    (marshal (marshalled-fx s))))
(def punmarshal (protected-fn unmarshal))

(defn from-hex (hexchars) #s of size 2
  (def [l m] (string/bytes hexchars))
    (+ (* l 16) m))

(defn netfn "Wrap a function around an adress for remote calling."
  (fx &opt host port maxbusize) # (*)=>*,s,s,n
  (default host "127.0.0.1")
  (default port 4055)
  (default maxbusize 4335)
  (def stream (net/listen host port))
  (def netfx  (protected-fn fx))
    (ev/spawn
      (forever (def connection (net/accept stream))
               (def this-busize (from-hex (net/read connection 2)))
               (assert (or (<= 0 this-busize maxbusize) (nil? this-busize)) )
               (def [succ vals]  (punmarshal (net/read connection (or this-busize maxbusize)) ))
               (def [succf valsf] (netfx ;vals))
                      (net/write connection (marshal valsf)) #([true|false(succ),*] 1)
                      (net/flush connection)
                      (net/close connection)
                      )))

(defn netcall "Call a netfunc."
  (&opt host port & args)
  (default port 4055)
  (default host "127.0.0.1")
  (def marshargs (marshal args))
  (def busize (length marshargs))
  (def stream (net/connect host port))
  (:write stream (string/from-bytes (div busize 16) (% busize 16)))
  (:write stream marshargs)
  (:flush stream)
  (def [succf valsf] (punmarshal (:read stream :all))) #Halts here for wait response.
  (net/close stream) #Close connection here.
  valsf
  )
